import base64
import os

from django_redis import get_redis_connection
from rest_framework import serializers

from oauth.models import OAuthQQUser
from oauth.utils import OAuthQQ
from users.models import User


class OAuthUserSerializer(serializers.ModelSerializer):
    """保存QQ用户序列化器"""
    sms_code = serializers.CharField(label="短信验证码", write_only=True)
    access_token = serializers.CharField(label="操作凭证", write_only=True)
    token = serializers.CharField(read_only=True)
    mobile = serializers.RegexField(label="手机号", regex=r'^(13\d|14[5|7]|15\d|166|17[3|6|7]|18\d)\d{8}$')

    class Meta:
        model = User
        fields = ('id', 'mobile', 'password', 'sms_code', 'access_token', 'username', 'token')
        extra_kwargs = {
            "username": {
                'read_only': True
            },
            "password": {
                "write_only": True,
                "min_length": 8,
                "max_length": 20,
                'error_messages': {
                    'min_length': '仅允许8-20个字符的密码',
                    'max_length': '仅允许8-20个字符的密码',
                }
            }
        }

    def validate(self, attrs):
        # 校验 access_token
        access_token = attrs['access_token']
        openid = OAuthQQ.check_save_user_token(access_token)
        if not openid:
            raise serializers.ValidationError("无效的access_token")
        attrs['openid'] = openid
        # 校验短信验证码
        mobile = attrs['mobile']
        sms_code = attrs['sms_code']
        redis_conn = get_redis_connection('verify_codes')
        real_sms_code = redis_conn.get('sms_%s' % mobile)
        if sms_code != real_sms_code.decode():
            raise serializers.ValidationError("短信验证码错误！")
        # 如果用户存在，检验密码是否正确
        try:
            user = User.objects.get(mobile=mobile)

        except Exception as e:
            user = None
        else:
            # 已经注册，校验密码
            password = attrs['password']
            if not user.check_password(password):
                raise serializers.ValidationError('用户密码错误！')
        # 给attrs中添加user数据，以便在保存绑定QQ登录用户的数据直接使用
        attrs['user'] = user
        return attrs

    def create(self, validated_data):
        """保存绑定QQ登录用户的数据并签发jwt token"""
        # 获取mobile和password
        mobile = validated_data['mobile']
        password = validated_data['password']

        # 如果`mobile`没有注册过，需要创建新用户
        user = validated_data['user']
        if user is None:
            username = base64.b64encode(os.urandom(6))
            user = User.objects.create_user(username=username, password=password, mobile=mobile)
        # 给视图对象增加user属性，保存绑定的用户的数据，以便在视图可以直接进行使用
        self.context['view'].user = user
        # 保存绑定QQ登录用户的数据
        openid = validated_data['openid']
        OAuthQQUser.objects.create(
            user=user,
            openid=openid
        )

        # 签发jwt token
        # 由服务器生成一个jwt token数据，包含登录用户身份信息
        from rest_framework_jwt.settings import api_settings
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
        # 生成载荷
        payload = jwt_payload_handler(user)
        # 生成jwt token
        token = jwt_encode_handler(payload)

        # 给user对象增加属性token，保存服务器签发jwt token数据
        user.token = token

        # 返回user
        return user
